Model
Revenue Recognition Problem
3 products: database (0, 30, 60), spreadsheet (0, 60, 90), word processor
- products (ID, name, type)
- contracts (ID, product, revenue, dateSigned)
- revenueRecognitions (contract, amount, recognizedOn)
Organizes business logic by procedures where each procedure handles a single request from the presentation.

Good when your application is simple
Example
First Script: calculates the amount of recognition due by a particular day
public ResultSet findRecognitionsFor
(long contractID
, MfDate asof
) throws SQLException
{
PreparedStatement stmt
= db
.prepareStatement
(findRecognitionsStatement
);
stmt
= db
.prepareStatement
(findRecognitionsStatement
);
stmt
.setLong
(1, contractID
);
stmt
.setDate
(2, asof
.toSqlDate
());
ResultSet result
= stmt
.executeQuery
();
return result
;
}
private static final String findRecognitionsStatement
=
"SELECT amount " +
"FROM revenueRecognitions " +
"WHERE contract = ? AND recognizedOn <= ?";
private Connection db
;
class RecognitionService
...
public Money recognizedRevenue
(long contractNumber
, MfDate asOf
) {
Money result
= Money
.dollars
(0);
try
{
ResultSet rs
= db
.findRecognitionsFor
(contractNumber
, asOf
);
while (rs
.next()) {
result
= result
.add
(Money
.dollars
(rs
.getBigDecimal
("amount")));
}
return result
;
}catch
(SQLException e
) {throw
new ApplicationException
(e
);
}
}
An object model of the domain that incorporates both behavior and data.

- simple Domain Model: mostly one domain object for each database table
- rich Domain Model: more complex with inheritance, etc
General vs. Specific Functions
The latter one need not be in the main class because it's not used frequently.
When to use?
When your application is complex enough
Example
class RevenueRecognition
...
private Money amount
;
private MfDate
date;
public RevenueRecognition
(Money amount
, MfDate
date) {
this
.amount
= amount
;
this
.date = date;
}
public Money getAmount
() {
return amount
;
}
boolean isRecognizableBy
(MfDate asOf
) {
return asOf
.after
(date) || asOf
.equals
(date);
}
class Contract
...
private List revenueRecognitions
= new ArrayList
();
public Money recognizedRevenue
(MfDate asOf
) {
Money result
= Money
.dollars
(0);
Iterator it
= revenueRecognitions
.iterator
();
while (it
.hasNext
()) {
RevenueRecognition r
= (RevenueRecognition
) it
.next();
if (r
.isRecognizableBy
(asOf
))
result
= result
.add
(r
.getAmount
());
}
return result
;
}
Makes it easy to add new revenueRecognition strategies.
Database access is also often done through separate classes.
A single instance that handles the business logic for all rows in a database table or view

Treats Relational Databases Better
It's not necessarily one table module per table.
Example
class TableModule...
protected DataTable table;
protected TableModule(DataSet ds, String tableName) {
table = ds.Tables[tableName];
}
Updating dataset:
class RevenueRecognition
...
public long Insert
(long contractID
, Decimal amount
, DateTime
date) {
DataRow newRow
= table
.NewRow
();
long id
= GetNextID
();
newRow
["ID"] = id
;
newRow
["contractID"] = contractID
;
newRow
["amount"] = amount
;
newRow
["date"]= String
.Format
("{0:s}", date);
table
.Rows
.Add
(newRow
);
return id
;
}
class RevenueRecognition...
public Decimal RecognizedRevenue (long contractID, DateTime asOf) {
String filter = String.Format("ContractID = {0}AND date <= #{1:d}#", contractID,
asOf);
DataRow[] rows = table.Select(filter);
Decimal result = 0m;
foreach (DataRow row in rows) {
result += (Decimal)row["amount"];
}
return result;
}
Application Logic vs. Domain Logic
The first is kind of independent of our application
Two implementation approaches:
- domain facade: separate classes
- operation script: parent class
Chapter 9 - Links And The Routing System
URLs
Two jobs:
- Server Instructions
- Part of Interface
They carry server instructions:
http://www.example.com/web/controller/article.php?id=123456&format_code=6532
Should they appear like this in browser status bar?
Drawbacks:
- Reveal Information
- Not SearchEngine friendly
- Small changes in application, e.g. script name changing
URLs as part of interface
http://www.example.com/articles/finance/2006/activity-breakdown.html
Routing System
Making Correspondence between internal and external URLs
// Internal URI syntax
<module>/<action>[?param1=value1][¶m2=value2][¶m3=value3]...
// Example internal URI, which never appears to the end user
article/permalink?year=2006&subject=finance&title=activity-breakdown
// Example external URL, which appears to the end user
http://www.example.com/articles/finance/2006/activity-breakdown.html
config/routing.yml
article_by_title:
url: articles/:subject/:year/:title.html
param: { module: article, action: permalink }
The mapping is performed by symfony's Routing system:
// The user types (or clicks on) this external URL
http://www.example.com/articles/finance/2006/activity-breakdown.html
// The front controller sees that it matches the article_by_title rule
// The routing system creates the following request parameters
'module' => 'article'
'action' => 'permalink'
'subject' => 'finance'
'year' => '2006'
'title' => 'activity-breakdown'
Reverse Routing
// The url_for() helper transforms an internal URI into an external URL
<a href="
<?php echo url_for
('article/permalink?subject=finance&year=2006&title=activity-breakdown') ?>">click here</a>
// The helper sees that the URI matches the article_by_title rule
// The routing system creates an external URL out of it
=> <a href="http://www.example.com/articles/finance/2006/activity-breakdown.html">click here</a>
// The link_to() helper directly outputs a hyperlink
// and avoids mixing PHP with HTML
<?php echo link_to
(
'click here',
'article/permalink?subject=finance&year=2006&title=activity-breakdown'
) ?>
// Internally, link_to() will make a call to url_for() so the result is the same
=> <a href="http://www.example.com/articles/finance/2006/activity-breakdown.html">click here</a>
URL Rewriting
prod:
.settings
no_script_name: off
and .htaccess is used to manage no-script-name URLs.
<IfModule mod_rewrite.c>
RewriteEngine On
# we skip all files with .something
RewriteCond %{REQUEST_URI} \..+$
RewriteCond %{REQUEST_URI} !\.html$
RewriteRule .* - [L]
# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
# no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
Hyperlinks, buttons and forms
// Hyperlink on a string
<?php echo link_to
('my article', 'article/read?title=Finance_in_France') ?>
=> <a href="/routed/url/to/Finance_in_France">my article</a>
// Hyperlink on an image
<?php echo link_to
(image_tag
('read.gif'), 'article/read?title=Finance_in_France') ?>
=> <a href="/routed/url/to/Finance_in_France"><img src="/images/read.gif" /></a>
// Button tag
<?php echo button_to
('my article', 'article/read?title=Finance_in_France') ?>
=> <input value="my article" type="button"onclick="document.location.href='/routed/url/to/Finance_in_France';" />
// Form tag
<?php echo form_tag
('article/read?title=Finance_in_France') ?>
=> <form method="post" action="/routed/url/to/Finance_in_France" />
Options
// Additional options as an associative array
<?php echo link_to
('my article', 'article/read?title=Finance_in_France', array(
'class' => 'foobar',
'target' => '_blank'http
://presentation.localhost/presentation/edit/id/14
)) ?>
// Additional options as a string (same result)
<?php echo link_to
('my article', 'article/read?title=Finance_in_France','class=foobar target=_blank') ?>
=> <a href="/routed/url/to/Finance_in_France" class="foobar" target="_blank">my article</a>
confirm and popup
<?php echo link_to
('delete item', 'item/delete?id=123', 'confirm=Are you sure?') ?>
=> <a onclick="return confirm('Are you sure?');"
href="/routed/url/to/delete/123.html">delete item</a>
<?php echo link_to
('add to cart', 'shoppingCart/add?id=100', 'popup=true') ?>
=> <a onclick="window.open(this.href);return false;"
href="/fo_dev.php/shoppingCart/add/id/100.html">add to cart</a>
<?php echo link_to
('add to cart', 'shoppingCart/add?id=100', array(
'popup' => array('popupWindow', 'width=310,height=400,left=320,top=0')
)) ?>
=> <a onclick="window.open(this.href,'popupWindow','width=310,height=400,left=320,top=0');return false;"
href="/fo_dev.php/shoppingCart/add/id/100.html">add to cart</a>
Fake POST
<?php echo link_to
('go to shopping cart', 'shoppingCart/add?id=100', 'post=true') ?>
=> <a onclick="f = document.createElement('form'); document.body.appendChild(f);
f.method = 'POST'; f.action = this.href; f.submit();return false;"
href="/shoppingCart/add/id/100.html">go to shopping cart</a>
Absolute Paths
<?php echo url_for
('article/read?title=Finance_in_France') ?>
=> '/routed/url/to/Finance_in_France'
<?php echo url_for
('article/read?title=Finance_in_France', true) ?>
=> 'http://www.example.com/routed/url/to/Finance_in_France'
<?php echo link_to
('finance', 'article/read?title=Finance_in_France') ?>
=> <a href="/routed/url/to/Finance_in_France">finance</a>
<?php echo link_to
('finance', 'article/read?title=Finance_in_France','absolute=true') ?>
=> <a href=" http://www.example.com/routed/url/to/Finance_in_France">finance</a>
// The same goes for the asset helpers
<?php echo image_tag
('test', 'absolute=true') ?>
<?php echo javascript_include_tag
('myscript', 'absolute=true') ?> apps/frontend/config/routing.yml
# default rules
homepage:
url: /
param: { module: default, action: index }
default_symfony:
url: /symfony/:action/*
param: { module: default }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
Constraint
article_by_id:
url: /article/:id
param: { module: article, action: read }
requirements: { id: \d+ }
Default Values
article_by_id:
url: /article/:id
param: { module: article, action: read, id: 1, display: true }
Routing by rule name
<?php echo link_to
('my article', 'article/read?id='.$article->getId()) ?>
// can also be written as
<?php echo link_to
('my article', '@article_by_id?id='.$article->getId()) ?> Adding Extension
prod:
routing:
param:
suffix: .html
or
article_list
:
url
: /latest_articles
param
: { module
: article
, action
: list }
article_list_feed
:
url
: /latest_articles
.rss
param
: { module
: article
, action
: list, type
: feed
}
Direct Creation of rules without routing.yml
sfContext
::getInstance()->getRouting()->prependRoute(
'article_by_id', // Route name
'/article/:id', // Route pattern
array('module' => 'article', 'action' => 'read'), // Default values
array('id' => '\d+'), // Requirements
); Accessing current route
// If you require a URL like
http://myapp.example.com/article/21
$routing = sfContext::getInstance()->getRouting();
// Use the following in article/read action
$uri = $routing->getCurrentInternalUri();
=> article/read?id=21
$uri = $routing->getCurrentInternalUri(true);
=> @article_by_id?id=21
$rule = $routing->getCurrentRouteName();
=> article_by_id
// If you just need the current module/action names,
// remember that they are actual request parameters
$module = $request->getParameter('module');
$action = $request->getParameter('action');
or
$uri = 'article/read?id=21';
$url = $this->getController()->genUrl($uri);
=> /article/21
$url = $this->getController()->genUrl($uri, true);
=> http://myapp.example.com/article/21